#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
文件转换模块 - 负责批量格式转换功能

这个模块提供了文件格式转换功能，包括：
1. 图片格式转换（JPG, PNG, GIF, BMP, TIFF等）
2. 文档格式转换（需要依赖特定的转换库）
3. 音频格式转换（需要依赖ffmpeg）
4. 视频格式转换（需要依赖ffmpeg）

使用示例：

1. 图片格式转换：
   ```python
   # 转换单个文件
   converter.convert_image("/path/image.jpg", "/path/image.png")
   
   # 批量转换
   converter.batch_convert_images("/path/to/images", "jpg", "png")
   ```

2. 音频格式转换：
   ```python
   # 转换单个音频文件
   converter.convert_audio("/path/audio.mp3", "/path/audio.wav")
   
   # 批量转换
   converter.batch_convert_audio("/path/to/audio_files", "mp3", "wav")
   ```

3. 视频格式转换：
   ```python
   # 转换单个视频文件
   converter.convert_video("/path/video.mp4", "/path/video.avi")
   
   # 批量转换
   converter.batch_convert_video("/path/to/videos", "mp4", "avi")
   ```
"""
import os
import sys
import subprocess
import shutil
from pathlib import Path
from typing import Dict, List, Optional, Tuple, Union, Any
import glob

# 尝试导入图片处理相关的依赖
try:
    from PIL import Image
    _has_pillow = True
except ImportError:
    _has_pillow = False

# 尝试导入文档处理相关的依赖
try:
    import docx
    import pdf2docx
    import pypdf
    _has_doc_converter = True
except ImportError:
    _has_doc_converter = False

def check_ffmpeg() -> bool:
    """
    检查系统是否安装了ffmpeg
    
    Returns:
        bool: 如果安装了ffmpeg返回True，否则返回False
    """
    try:
        subprocess.run(
            ["ffmpeg", "-version"], 
            stdout=subprocess.PIPE, 
            stderr=subprocess.PIPE,
            check=False
        )
        return True
    except FileNotFoundError:
        return False

def convert_image(input_file: Union[str, Path], output_file: Union[str, Path], 
                quality: int = 90) -> bool:
    """
    转换图片格式
    
    Args:
        input_file: 输入图片文件路径
        output_file: 输出图片文件路径
        quality: 输出图片质量 (仅用于JPG等有损格式)
        
    Returns:
        bool: 操作是否成功
    """
    if not _has_pillow:
        raise ImportError("此功能需要安装Pillow库: pip install Pillow")
    
    input_path = Path(input_file)
    output_path = Path(output_file)
    
    if not input_path.exists():
        raise ValueError(f"输入文件不存在: {input_file}")
    
    # 确保输出目录存在
    output_path.parent.mkdir(parents=True, exist_ok=True)
    
    try:
        # 打开图像并转换
        with Image.open(input_path) as img:
            # 处理特殊格式变换
            if output_path.suffix.lower() in ['.jpg', '.jpeg']:
                # 处理alpha通道(RGBA转RGB)
                if img.mode == 'RGBA':
                    # 白色背景合成
                    background = Image.new('RGB', img.size, (255, 255, 255))
                    background.paste(img, mask=img.split()[3])
                    img = background
            
            # 保存图像，根据扩展名自动判断格式
            if output_path.suffix.lower() in ['.jpg', '.jpeg']:
                img.save(output_path, quality=quality, optimize=True)
            else:
                img.save(output_path)
        
        return True
    except Exception as e:
        print(f"转换图片时出错: {e}")
        return False

def batch_convert_images(directory: Union[str, Path], from_format: str, to_format: str,
                       recursive: bool = False, output_dir: Optional[Union[str, Path]] = None,
                       quality: int = 90) -> Dict[str, str]:
    """
    批量转换图片格式
    
    Args:
        directory: 要处理的目录
        from_format: 源文件格式（如'jpg', 'png'）
        to_format: 目标文件格式（如'png', 'webp'）
        recursive: 是否递归处理子目录
        output_dir: 输出目录，默认为原目录
        quality: 输出图片质量 (仅用于JPG等有损格式)
        
    Returns:
        Dict[str, str]: 映射，键为原始路径，值为转换后的路径
    """
    if not _has_pillow:
        raise ImportError("此功能需要安装Pillow库: pip install Pillow")
    
    directory_path = Path(directory)
    if not directory_path.exists() or not directory_path.is_dir():
        raise ValueError(f"目录不存在或不是文件夹: {directory}")
    
    # 规范化格式
    from_format = from_format.lower().strip('.')
    to_format = to_format.lower().strip('.')
    
    # 准备文件匹配模式
    pattern = f"*.{from_format}"
    
    # 设置输出目录
    if output_dir is None:
        output_path = directory_path
    else:
        output_path = Path(output_dir)
        output_path.mkdir(parents=True, exist_ok=True)
    
    # 查找匹配的文件
    if recursive:
        matched_files = list(directory_path.rglob(pattern))
    else:
        matched_files = list(directory_path.glob(pattern))
    
    converted_map = {}
    
    for file_path in matched_files:
        if file_path.is_file():
            # 确定输出文件路径
            if output_path == directory_path:
                # 在相同目录输出
                output_file = file_path.with_suffix(f".{to_format}")
            else:
                # 在输出目录创建相同的目录结构
                rel_path = file_path.relative_to(directory_path)
                output_file = output_path / rel_path.with_suffix(f".{to_format}")
                
                # 确保父目录存在
                output_file.parent.mkdir(parents=True, exist_ok=True)
            
            # 转换图片
            success = convert_image(file_path, output_file, quality)
            
            if success:
                converted_map[str(file_path)] = str(output_file)
    
    return converted_map

def convert_audio(input_file: Union[str, Path], output_file: Union[str, Path],
                bitrate: Optional[str] = None) -> bool:
    """
    转换音频格式（使用ffmpeg）
    
    Args:
        input_file: 输入音频文件路径
        output_file: 输出音频文件路径
        bitrate: 音频比特率 (如"128k", "320k")
        
    Returns:
        bool: 操作是否成功
    """
    if not check_ffmpeg():
        raise EnvironmentError("此功能需要安装ffmpeg: https://ffmpeg.org/download.html")
    
    input_path = Path(input_file)
    output_path = Path(output_file)
    
    if not input_path.exists():
        raise ValueError(f"输入文件不存在: {input_file}")
    
    # 确保输出目录存在
    output_path.parent.mkdir(parents=True, exist_ok=True)
    
    try:
        cmd = ["ffmpeg", "-i", str(input_path)]
        
        # 添加比特率参数（如果提供）
        if bitrate:
            cmd.extend(["-b:a", bitrate])
        
        # 设置静默模式和覆盖已存在文件
        cmd.extend(["-y", "-loglevel", "error", str(output_path)])
        
        result = subprocess.run(
            cmd,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True,
            check=False
        )
        
        # 检查转换是否成功
        if result.returncode != 0:
            print(f"转换音频时出错: {result.stderr}")
            return False
        
        return True
    except Exception as e:
        print(f"转换音频时出错: {e}")
        return False

def batch_convert_audio(directory: Union[str, Path], from_format: str, to_format: str,
                      recursive: bool = False, output_dir: Optional[Union[str, Path]] = None,
                      bitrate: Optional[str] = None) -> Dict[str, str]:
    """
    批量转换音频格式
    
    Args:
        directory: 要处理的目录
        from_format: 源文件格式（如'mp3', 'wav'）
        to_format: 目标文件格式（如'wav', 'ogg'）
        recursive: 是否递归处理子目录
        output_dir: 输出目录，默认为原目录
        bitrate: 音频比特率 (如"128k", "320k")
        
    Returns:
        Dict[str, str]: 映射，键为原始路径，值为转换后的路径
    """
    if not check_ffmpeg():
        raise EnvironmentError("此功能需要安装ffmpeg: https://ffmpeg.org/download.html")
    
    directory_path = Path(directory)
    if not directory_path.exists() or not directory_path.is_dir():
        raise ValueError(f"目录不存在或不是文件夹: {directory}")
    
    # 规范化格式
    from_format = from_format.lower().strip('.')
    to_format = to_format.lower().strip('.')
    
    # 准备文件匹配模式
    pattern = f"*.{from_format}"
    
    # 设置输出目录
    if output_dir is None:
        output_path = directory_path
    else:
        output_path = Path(output_dir)
        output_path.mkdir(parents=True, exist_ok=True)
    
    # 查找匹配的文件
    if recursive:
        matched_files = list(directory_path.rglob(pattern))
    else:
        matched_files = list(directory_path.glob(pattern))
    
    converted_map = {}
    
    for file_path in matched_files:
        if file_path.is_file():
            # 确定输出文件路径
            if output_path == directory_path:
                # 在相同目录输出
                output_file = file_path.with_suffix(f".{to_format}")
            else:
                # 在输出目录创建相同的目录结构
                rel_path = file_path.relative_to(directory_path)
                output_file = output_path / rel_path.with_suffix(f".{to_format}")
                
                # 确保父目录存在
                output_file.parent.mkdir(parents=True, exist_ok=True)
            
            # 转换音频
            success = convert_audio(file_path, output_file, bitrate)
            
            if success:
                converted_map[str(file_path)] = str(output_file)
    
    return converted_map

def convert_video(input_file: Union[str, Path], output_file: Union[str, Path],
                video_bitrate: Optional[str] = None,
                audio_bitrate: Optional[str] = None,
                resolution: Optional[str] = None) -> bool:
    """
    转换视频格式（使用ffmpeg）
    
    Args:
        input_file: 输入视频文件路径
        output_file: 输出视频文件路径
        video_bitrate: 视频比特率 (如"1M", "2M")
        audio_bitrate: 音频比特率 (如"128k")
        resolution: 分辨率 (如"1280x720")
        
    Returns:
        bool: 操作是否成功
    """
    if not check_ffmpeg():
        raise EnvironmentError("此功能需要安装ffmpeg: https://ffmpeg.org/download.html")
    
    input_path = Path(input_file)
    output_path = Path(output_file)
    
    if not input_path.exists():
        raise ValueError(f"输入文件不存在: {input_file}")
    
    # 确保输出目录存在
    output_path.parent.mkdir(parents=True, exist_ok=True)
    
    try:
        cmd = ["ffmpeg", "-i", str(input_path)]
        
        # 添加分辨率参数（如果提供）
        if resolution:
            cmd.extend(["-s", resolution])
        
        # 添加视频比特率参数（如果提供）
        if video_bitrate:
            cmd.extend(["-b:v", video_bitrate])
        
        # 添加音频比特率参数（如果提供）
        if audio_bitrate:
            cmd.extend(["-b:a", audio_bitrate])
        
        # 设置静默模式和覆盖已存在文件
        cmd.extend(["-y", "-loglevel", "error", str(output_path)])
        
        result = subprocess.run(
            cmd,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True,
            check=False
        )
        
        # 检查转换是否成功
        if result.returncode != 0:
            print(f"转换视频时出错: {result.stderr}")
            return False
        
        return True
    except Exception as e:
        print(f"转换视频时出错: {e}")
        return False

def batch_convert_video(directory: Union[str, Path], from_format: str, to_format: str,
                      recursive: bool = False, output_dir: Optional[Union[str, Path]] = None,
                      video_bitrate: Optional[str] = None,
                      audio_bitrate: Optional[str] = None,
                      resolution: Optional[str] = None) -> Dict[str, str]:
    """
    批量转换视频格式
    
    Args:
        directory: 要处理的目录
        from_format: 源文件格式（如'mp4', 'avi'）
        to_format: 目标文件格式（如'mkv', 'webm'）
        recursive: 是否递归处理子目录
        output_dir: 输出目录，默认为原目录
        video_bitrate: 视频比特率 (如"1M", "2M")
        audio_bitrate: 音频比特率 (如"128k")
        resolution: 分辨率 (如"1280x720")
        
    Returns:
        Dict[str, str]: 映射，键为原始路径，值为转换后的路径
    """
    if not check_ffmpeg():
        raise EnvironmentError("此功能需要安装ffmpeg: https://ffmpeg.org/download.html")
    
    directory_path = Path(directory)
    if not directory_path.exists() or not directory_path.is_dir():
        raise ValueError(f"目录不存在或不是文件夹: {directory}")
    
    # 规范化格式
    from_format = from_format.lower().strip('.')
    to_format = to_format.lower().strip('.')
    
    # 准备文件匹配模式
    pattern = f"*.{from_format}"
    
    # 设置输出目录
    if output_dir is None:
        output_path = directory_path
    else:
        output_path = Path(output_dir)
        output_path.mkdir(parents=True, exist_ok=True)
    
    # 查找匹配的文件
    if recursive:
        matched_files = list(directory_path.rglob(pattern))
    else:
        matched_files = list(directory_path.glob(pattern))
    
    converted_map = {}
    
    for file_path in matched_files:
        if file_path.is_file():
            # 确定输出文件路径
            if output_path == directory_path:
                # 在相同目录输出
                output_file = file_path.with_suffix(f".{to_format}")
            else:
                # 在输出目录创建相同的目录结构
                rel_path = file_path.relative_to(directory_path)
                output_file = output_path / rel_path.with_suffix(f".{to_format}")
                
                # 确保父目录存在
                output_file.parent.mkdir(parents=True, exist_ok=True)
            
            # 转换视频
            success = convert_video(
                file_path, output_file,
                video_bitrate=video_bitrate,
                audio_bitrate=audio_bitrate,
                resolution=resolution
            )
            
            if success:
                converted_map[str(file_path)] = str(output_file)
    
    return converted_map

def convert_document(input_file: Union[str, Path], output_file: Union[str, Path]) -> bool:
    """
    转换文档格式
    
    Args:
        input_file: 输入文档文件路径
        output_file: 输出文档文件路径
        
    Returns:
        bool: 操作是否成功
    
    支持的转换:
    - DOCX 到 PDF
    - PDF 到 DOCX
    """
    if not _has_doc_converter:
        raise ImportError("此功能需要安装文档转换库: pip install python-docx pdf2docx PyPDF2")
    
    input_path = Path(input_file)
    output_path = Path(output_file)
    
    if not input_path.exists():
        raise ValueError(f"输入文件不存在: {input_file}")
    
    # 确保输出目录存在
    output_path.parent.mkdir(parents=True, exist_ok=True)
    
    # 获取文件扩展名
    input_ext = input_path.suffix.lower()
    output_ext = output_path.suffix.lower()
    
    try:
        # DOCX 到 PDF
        if input_ext == '.docx' and output_ext == '.pdf':
            from docx2pdf import convert
            convert(str(input_path), str(output_path))
            return True
        
        # PDF 到 DOCX
        elif input_ext == '.pdf' and output_ext == '.docx':
            from pdf2docx import Converter
            cv = Converter(str(input_path))
            cv.convert(str(output_path))
            cv.close()
            return True
        
        else:
            raise ValueError(f"不支持的转换: {input_ext} 到 {output_ext}")
    
    except Exception as e:
        print(f"转换文档时出错: {e}")
        return False

def batch_convert_documents(directory: Union[str, Path], from_format: str, to_format: str,
                          recursive: bool = False, 
                          output_dir: Optional[Union[str, Path]] = None) -> Dict[str, str]:
    """
    批量转换文档格式
    
    Args:
        directory: 要处理的目录
        from_format: 源文件格式（如'docx', 'pdf'）
        to_format: 目标文件格式（如'pdf', 'docx'）
        recursive: 是否递归处理子目录
        output_dir: 输出目录，默认为原目录
        
    Returns:
        Dict[str, str]: 映射，键为原始路径，值为转换后的路径
    """
    if not _has_doc_converter:
        raise ImportError("此功能需要安装文档转换库: pip install python-docx pdf2docx PyPDF2 docx2pdf")
    
    directory_path = Path(directory)
    if not directory_path.exists() or not directory_path.is_dir():
        raise ValueError(f"目录不存在或不是文件夹: {directory}")
    
    # 规范化格式
    from_format = from_format.lower().strip('.')
    to_format = to_format.lower().strip('.')
    
    # 准备文件匹配模式
    pattern = f"*.{from_format}"
    
    # 设置输出目录
    if output_dir is None:
        output_path = directory_path
    else:
        output_path = Path(output_dir)
        output_path.mkdir(parents=True, exist_ok=True)
    
    # 查找匹配的文件
    if recursive:
        matched_files = list(directory_path.rglob(pattern))
    else:
        matched_files = list(directory_path.glob(pattern))
    
    converted_map = {}
    
    for file_path in matched_files:
        if file_path.is_file():
            # 确定输出文件路径
            if output_path == directory_path:
                # 在相同目录输出
                output_file = file_path.with_suffix(f".{to_format}")
            else:
                # 在输出目录创建相同的目录结构
                rel_path = file_path.relative_to(directory_path)
                output_file = output_path / rel_path.with_suffix(f".{to_format}")
                
                # 确保父目录存在
                output_file.parent.mkdir(parents=True, exist_ok=True)
            
            # 转换文档
            success = convert_document(file_path, output_file)
            
            if success:
                converted_map[str(file_path)] = str(output_file)
    
    return converted_map 